/*
 * COPYRIGHT:	2010 Christopher Smith, licenced under the GNU GPL v2; see COPYING in the top level directory
 * PROJECT:		sdedit
 * FILE:		sdedit rtl.cpp
 * PURPOSE:		support functions for sdedit specific structures
 */

BOOL DoesAclContainAceMatcingPredicate(ACL const *SdAcl, UCHAR (*Pred)(IN UCHAR Type))
{
	if(!SdAcl)
		return FALSE;

	ptr_buffer<ACE> pb(c_cast<ACE *>(SdAcl));

	pb.pos += sizeof *SdAcl;

	for(USHORT i = 0; i < SdAcl->AceCount; i++)
	{
		if(Pred(pb.data->Type))
			return TRUE;

		pb.pos += pb.data->Size;
	}

	return FALSE;
}

ULONG CountAcesOfType(ACL const *SdAcl, UCHAR Type)
{
	ULONG Count = 0;

	if(!SdAcl)
		return FALSE;

	ptr_buffer<ACE> pb(c_cast<ACE *>(SdAcl));

	pb.pos += sizeof *SdAcl;

	for(USHORT i = 0; i < SdAcl->AceCount; i++)
	{
		if(pb.data->Type == Type)
			Count++;

		pb.pos += pb.data->Size;
	}

	return Count;
}

SECURITY_INFORMATION ModifiedSdEditStateSecInfo(SD_EDIT_STATE const &Es)
{
	SECURITY_INFORMATION Si = 0;

	if(Es.Owner.State & SID_FLAG_CHANGED)
		Si |= OWNER_SECURITY_INFORMATION;

	if(Es.Group.State & SID_FLAG_CHANGED)
		Si |= GROUP_SECURITY_INFORMATION;

	if(Es.Dacl.State & ACL_FLAG_CHANGED)
		Si |= DACL_SECURITY_INFORMATION;

	if(Es.Sacl.State & ACL_FLAG_CHANGED)
		Si |= SACL_SECURITY_INFORMATION;

	if(Es.Lacl.State & ACL_FLAG_CHANGED)
		Si |= LABEL_SECURITY_INFORMATION;

	return Si;
}

BOOLEAN FORCEINLINE IsStdAce(UCHAR Type)
{
	switch(Type)
	{
		case ACCESS_ALLOWED_ACE_TYPE:
		case ACCESS_DENIED_ACE_TYPE:
		case SYSTEM_AUDIT_ACE_TYPE:
		case SYSTEM_ALARM_ACE_TYPE:
		case ACCESS_ALLOWED_CALLBACK_ACE_TYPE:
		case ACCESS_DENIED_CALLBACK_ACE_TYPE:
		case SYSTEM_AUDIT_CALLBACK_ACE_TYPE:
		case SYSTEM_ALARM_CALLBACK_ACE_TYPE:
		case SYSTEM_MANDATORY_LABEL_ACE_TYPE:
			return TRUE;

		default:
			return FALSE;
	}
}

BOOLEAN FORCEINLINE IsObjectAce(UCHAR Type)
{
	switch(Type)
	{
		case ACCESS_ALLOWED_OBJECT_ACE_TYPE:
		case ACCESS_DENIED_OBJECT_ACE_TYPE:
		case SYSTEM_AUDIT_OBJECT_ACE_TYPE:
		case SYSTEM_ALARM_OBJECT_ACE_TYPE:
		case ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE:
		case ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE:
		case SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE:
		case SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE:
			return TRUE;

		default:
			return FALSE;
	}
}

BOOLEAN FORCEINLINE IsLabelAce(UCHAR Type)
{
	switch(Type)
	{
	case SYSTEM_MANDATORY_LABEL_ACE_TYPE:
		return TRUE;

	default:
		return FALSE;
	}
}

BOOLEAN FORCEINLINE IsNonLabelAce(UCHAR Type)
{
	return !IsLabelAce(Type);
}

ACCESS_MASK MapGenericBits(ACCESS_MASK Mask, GENERIC_MAPPING Gm)
{
	if(Mask & GENERIC_READ)
		Mask |= Gm.GenericRead;

	if(Mask & GENERIC_WRITE)
		Mask |= Gm.GenericWrite;

	if(Mask & GENERIC_EXECUTE)
		Mask |= Gm.GenericExecute;

	if(Mask & GENERIC_ALL)
		Mask |= Gm.GenericAll;

	Mask &= ~(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL);

	return Mask;
}

void MapAclGenericBits(ACL *Acl, GENERIC_MAPPING Gm, USHORT AceFlagsReq = 0xFFFF)
{
	ptr_buffer<ACE> pb(c_cast<ACE *>(Acl));
	
	pb.pos += sizeof(ACL);

	for(USHORT i = 0; i < Acl->AceCount; i++)
	{
		if(pb.data->Flags & AceFlagsReq)
			pb.data->Mask = MapGenericBits(pb.data->Mask, Gm);
		pb.pos += pb.data->Size;
	}
}

BOOLEAN AreAcesEffectivelyEqual(ACE const *Ace1, ACE const *Ace2, GENERIC_MAPPING Gm)
{
	if(Ace1->Size != Ace2->Size)
		return FALSE;

	if(Ace1->Type != Ace2->Type)
		return FALSE;

	//if(Ace1->Revision != Ace2->Revision)
	//	return FALSE;

	if(MapGenericBits(Ace1->Mask, Gm) != MapGenericBits(Ace2->Mask, Gm))
		return FALSE;

	if(memcmp(Ace1->Body, Ace2->Body, Ace1->Size - sizeof(SDE_ACE_HEADER)) != 0) //compare bodies, including the Sid and any object properties
		return FALSE;

	return TRUE;
}

BOOLEAN AreAclsEffectivelyEqual(ACL const *Acl1, ACL const *Acl2, GENERIC_MAPPING Gm)
{
	USHORT RelevantFlags = ACE_FLAG_OBJECT_INHERIT | ACE_FLAG_CONTAINER_INHERIT | ACE_FLAG_NO_PROPAGATE | ACE_FLAG_INHERIT_ONLY | ACE_FLAG_INHERITED;

	if(Acl1->AclSize != Acl2->AclSize)
		return FALSE;

	if(Acl1->AceCount != Acl2->AceCount)
		return FALSE;

	ptr_buffer<ACE const> pb1(c_cast<ACE const *>(Acl1)), pb2(c_cast<ACE const *>(Acl2));

	pb1.pos += sizeof(ACL);
	pb2.pos += sizeof(ACL);

	for(USHORT i = 0; i < Acl1->AceCount; i++)
	{
		if(!AreAcesEffectivelyEqual(pb1.data, pb2.data, Gm))
			return FALSE;

		if((pb1.data->Flags & RelevantFlags) != (pb2.data->Flags & RelevantFlags))
			return FALSE;

		pb1.pos = pb2.pos += pb1.data->Size; //AreAcesEffectivelyEqual does require the sizes of each be equal
	}

	return TRUE;
}

BOOLEAN IsSidTypeInMask(SID_NAME_USE Type, ULONG Mask)
{
	switch(Type)
	{
	case SidTypeUser:
		return (Mask & SID_TYPE_USER) != 0;
	case SidTypeGroup:
		return (Mask & SID_TYPE_GROUP) != 0;
	case SidTypeAlias:
		return (Mask & SID_TYPE_ALIAS) != 0;
	case SidTypeWellKnownGroup:
		return (Mask & SID_TYPE_WELL_KNOWN) != 0;
	case SidTypeDeletedAccount:
		return (Mask & SID_TYPE_DELETED) != 0;
	case SidTypeInvalid:
		return (Mask & SID_TYPE_INVALID )!= 0;
	case SidTypeUnknown:
		return (Mask & SID_TYPE_UNKNOWN) != 0;
	case SidTypeComputer:
		return (Mask & SID_TYPE_COMPUTER) != 0;
	default:
		return TRUE;
	}
}

BOOLEAN AreLabelsSupportedLocally()
{
	OSVERSIONINFO Vi;
	Vi.dwOSVersionInfoSize = sizeof Vi;

	GetVersionEx(&Vi);

	return Vi.dwPlatformId == VER_PLATFORM_WIN32_NT && Vi.dwMajorVersion >= 6;
}